import { KubernetesObject } from "@kubernetes/client-node"
import ContainerFactory from "@yam-plugin/core-deploy/handlers/container"
import type { DeployWorkload } from "@yam-plugin/core-deploy/types.d"
import { KV, OperateFunction, PlanContextData } from "yam-types"
import { AccessApproachConfig } from "../types"
import { composeIngress } from "./ingress"
import { composeHeadlessSvc, composeLoadBalancer, composeService } from "./service"


const accessOperator: OperateFunction<AccessApproachConfig> = async (plan, diff, settings) => {
  if (diff.currentItems.length === 0) {
    plan.log.info(`'access' is not configured, operation skipped`)
    return
  }
  if (!diff.hasDiff) {
    plan.log.info(`'access' is not changed, operation skipped`)
  }

  for (const access of diff.currentItems) {
    composeServiceAndIngress(access, plan.data, settings).forEach(r => {
      plan.action(async (ctx) => {
        await ctx.mergeToYaml({
          name: r.metadata?.name + '-' + r.kind,
          content: r
        })
      }, `create-${r.kind?.toLowerCase()}:${r.metadata?.name}`)
    })
  }

  if (diff.deletedItems.length > 0) {
    for (const access of diff.deletedItems) {
      composeServiceAndIngress(access, plan.data, settings).forEach(r => {
        plan.action(async (ctx) => {
          await ctx.k8sClient.delete({
            name: r.metadata?.name || '',
            kind: r.kind || ''
          })
        }, `delete-${r.kind?.toLowerCase()}:${r.metadata?.name}`)
      })
    }
  }
}

const composeServiceAndIngress = (appAccess: AccessApproachConfig, planData: PlanContextData, settings: KV): KubernetesObject[] => {
  const result: KubernetesObject[] = []
  const workload = planData.currentModelFull.deploy as DeployWorkload | undefined

  const containerPort = ContainerFactory.generatePortSpec(appAccess.containerPort || workload?.containers[0].port || 80)[0]

  // create Kubernetes ClusterIP/NodePort Service
  const serviceName = appAccess.serviceName || planData.workloadName
  const servicePort = appAccess.servicePort || containerPort.containerPort
  const nodePort = appAccess.advancedFeatures?.nodePort
  result.push(composeService(planData, appAccess, serviceName, servicePort, containerPort, nodePort))

  // create LoadBalancer service
  if (appAccess.loadBalancerEnabled) {
    result.push(composeLoadBalancer(planData, appAccess, serviceName, servicePort, containerPort, settings))
  }

  // create None-ClusterIP headless service
  if (appAccess.headlessSvcEnabled) {
    result.push(composeHeadlessSvc(planData, appAccess, serviceName, servicePort, containerPort))
  }

  // create Ingress resource
  if (appAccess.ingressEnabled) {
    result.push(composeIngress(planData, appAccess, containerPort, servicePort, settings))
  }
  return result
}

export default accessOperator